﻿#region

using System;
using System.Linq;
using System.Reflection;
using Castle.DynamicProxy;
using OpenQA.Selenium;
using Selenium.PageToolkit.ControlFactoryChainImpl;

#endregion

namespace Selenium.PageToolkit
{
    public class LambdaPageFactory
    {
        internal static readonly ProxyGenerator Generator = new ProxyGenerator();
        public Func<ISearchContext> DriverFactory;

        private IControlFactoryChain GetInjectionChain()
        {
            return new WebElementFactory()
                {
                    Next = new WebElementCollectionFactory()
                        {
                            Next = new ControlCollectionFactory()
                                {
                                    Next = new ControlFactory()
                                }
                        }
                };
        }

        public void Inject(object pageObject)
        {
            var injectionChain = GetInjectionChain();
            var supportedAttributeTypes = injectionChain.GetSupportedAttributeTypes().Distinct().ToList();
            
            var type = pageObject.GetType();
            foreach (var memberInfo in type.GetMembers())
            {
                var elementAttributes = supportedAttributeTypes.SelectMany(a => memberInfo.GetCustomAttributes(a, true));
                var attribute = elementAttributes.FirstOrDefault();

                if (attribute != null)
                {
                    var fieldType = GetMemberReturnType(memberInfo);
                    if (fieldType == null)
                        continue;
                    var value = injectionChain.Inject(fieldType, attribute, this);
                    if(value == null)
                        continue;
                    Assign(pageObject, memberInfo, value);
                  
                }
            }
        }

      internal object CreateWebElement(By spec)
        {
            var interceptor = new WebElementInterceptor(DriverFactory, spec);
            var elementProxy = Generator.CreateInterfaceProxyWithoutTarget(typeof (IWebElement),
                                                                            interceptor);
            return elementProxy;
        }

        private void Assign(object pageObject, MemberInfo memberInfo, object elementProxy)
        {
            var setter = GetSetter(memberInfo);
            if (setter != null)
                setter(pageObject, elementProxy);
        }

        public static Type GetMemberReturnType(MemberInfo memberInfo)
        {
            var propertyInfo = memberInfo as PropertyInfo;
            var fieldInfo = memberInfo as FieldInfo;
            if (propertyInfo != null)
                return propertyInfo.PropertyType;
            else if (fieldInfo != null)
                return fieldInfo.FieldType;
            return null;
        }

        private static Action<object, object> GetSetter(MemberInfo memberInfo)
        {
            var propertyInfo = memberInfo as PropertyInfo;
            var fieldInfo = memberInfo as FieldInfo;
            if (propertyInfo != null)
                return propertyInfo.SetValue;
            else if (fieldInfo != null)
                return fieldInfo.SetValue;
            return null;
        }
    }
}